import allure
import jsonpath
from numpy.random.mtrand import operator
import operator
from common.recordlog import logs
from common.connection import ConnectMysql
import json

class Assertions:
    """"
    接口断言模式，支持
    1）响应文本字符串包含模式断言
    2）响应结果相等断言
    3）响应结果不相等断言
    4）响应结果任意值断言
    5）数据库断言

    """

    def eq_html_title(self, validation, response_text):
        logs.error(f"validation:{validation}")
        for ex in validation:
            for ex_key, ex_value in ex.items():
                logs.error(f"ex_value:{ex_value}")
                value = ex_value["title"]
                logs.error(f"value:{value}")
                logs.error(type(response_text))
                if f"<title>{value}</title>" in response_text:
                    assert True
                else:
                    assert False



    def eq_status_code(self, value, status_code):

        # 设置标记，若flag不为0,说明存在断言失败
        flag = 0
        # 将字典的key和value拆分开
        for assert_key,assert_value in value.items():
            # 判断key是否为status_code
            if assert_key == "status_code":
                if assert_value != status_code:
                    flag += 1
                    allure.attach(f"预期结果：{assert_value},实际结果：{status_code}","状态码断言失败",allure.attachment_type.TEXT)
                    logs.error(f"eq_status_code断言失败：接口状态码{status_code}不等于预期状态码{assert_value}")
                else:
                    allure.attach(f"预期结果：{assert_value},实际结果：{status_code}", "状态码断言成功",
                                  allure.attachment_type.TEXT)
                    logs.info(f"eq_status_code断言成功：接口状态码{status_code}等于预期状态码{assert_value}")

            else:
                allure.attach(f"key不为status_code 请检查key:{assert_key}", "状态码断言失败",allure.attachment_type.TEXT)
                logs.error(f"eq_status_code断言失败 key不为status_code 请检查key:{assert_key}")
                flag += 1
        return flag

    # 包含断言：判断预期结果是否包含于响应结果字符串中
    def contains_assert(self, value, response):
        """
        字符串包含断言模式，断言预期结果的字符串是否包含在接口的响应信息中
        :param value: 预期结果，yaml文件的预期结果值
        :param response: 接口实际响应结果
        :return: 返回结果的状态标识
        """
        # 设置标记，若flag不为0,说明存在断言失败
        flag = 0
        # 将字典的key和value拆分开
        for assert_key,assert_value in value.items():

            # 从嵌套的 JSON 数据中递归搜索匹配 assert_key 的所有值 | 成功时返回 列表（即使只匹配一个值 | 无匹配时返回 False（注意：不是空列表)
            resp_list = jsonpath.jsonpath(response,f"$..{assert_key}")
            # 判断响应结果是否为字符串列表
            if resp_list != False and isinstance(resp_list,str):
                 # 将字符串列表转化为字符串
                resp_list = "".join(resp_list)
                if assert_value in resp_list:
                    allure.attach(f"预期结果：{assert_value},实际结果：{resp_list}", "包含断言成功",allure.attachment_type.TEXT)
                    logs.info(f"contains_assert断言成功：预期的{assert_key} = {assert_value},响应结果为：{resp_list}")
                else:
                    flag +=1
                    allure.attach(f"预期结果：{assert_value},实际结果：{resp_list}","包含断言失败",allure.attachment_type.TEXT)
                    logs.info(f"contains_assert断言失败：预期的{assert_key} != {assert_value},响应结果为：{resp_list}")

        return flag


    # 相等断言：判断预期结果是否等于响应结果
    def equal_assert(self, expected_results, actual_results):
        """
        相等断言模式
        :param expected_results: 预期结果，yaml文件validation值
        :param actual_results: 接口实际响应结果
        :return:
        """
        flag = 0
        try:
            for key,value in expected_results.items():
                act_value = jsonpath.jsonpath(actual_results, f"$..{key}")[0]
                if str(act_value) == str(value):
                    allure.attach(f"equal_assert断言成功：预期的{key} = {value}",
                              "相等断言成功", allure.attachment_type.TEXT)
                    logs.info(
                    f"equal_assert断言成功:预期的{key} = {act_value},响应结果为：{actual_results}")
                else:
                    flag += 1
                    allure.attach(f"equal_assert断言成功：预期的{key} ！= {value}",
                              "相等断言失败", allure.attachment_type.TEXT)
                    logs.info(
                    f"equal_assert断言失败:预期的{key} ！= {act_value},响应结果为：{actual_results}")
            return flag
        except Exception as e:
            flag += 1
            logs.error(f"相等断言异常：{e}")
            return flag


    # # 相等断言：判断预期结果是否等于响应结果
    # def equal_assert(self, expected_results, actual_results):
    #     """
    #     相等断言模式
    #     :param expected_results: 预期结果，yaml文件validation值
    #     :param actual_results: 接口实际响应结果
    #     :return:
    #     """
    #     flag = 0
    #     # 判断预期结果和响应结果是否都为字符串
    #     if isinstance(expected_results,dict) and isinstance(actual_results,dict):
    #         # 找出预期结果和响应结果公共的key，取列表中的第一个值
    #         common_key = list(expected_results.keys() & actual_results.keys())[0]
    #         # 用公共的key去响应结果中找到对应的值，然后用公共的key和值组成新的字典
    #         new_actual_results = {common_key:actual_results[common_key]}
    #         # 比较新的字典和预期结果字典是否相等 | operator.eq(new_actual_results, expected_results) 是用于比较两个对象是否相等的函数式写法
    #         eq_assert = operator.eq(new_actual_results,expected_results)
    #         if eq_assert:
    #             allure.attach(f"equal_assert断言成功：预期的{common_key} = {expected_results[common_key]}","相等断言成功",allure.attachment_type.TEXT)
    #             logs.info(f"equal_assert断言成功:预期的{common_key} = {new_actual_results[common_key]},响应结果为：{actual_results}")
    #         else:
    #             flag += 1
    #             allure.attach(f"equal_assert断言成功：预期的{common_key} ！= {expected_results[common_key]}","相等断言失败", allure.attachment_type.TEXT)
    #             logs.info(f"equal_assert断言失败:预期的{common_key} ！= {expected_results[common_key]},响应结果为：{actual_results}")
    #     else:
    #         raise TypeError('相等断言--类型错误，预期结果和接口实际响应结果必须为字典类型！')
    #     return flag

    def not_equal_assert(self, expected_results, actual_results):
        """
        不相等断言模式
        :param expected_results: 预期结果，yaml文件validation值
        :param actual_results: 接口实际响应结果
        :return:
        """
        flag = 0
        # 判断预期结果和响应结果是否都为字符串
        if isinstance(expected_results, dict) and isinstance(actual_results, dict):
            # 找出预期结果和响应结果公共的key，取列表中的第一个值
            common_key = list(expected_results.keys() & actual_results.keys())[0]
            # 用公共的key去响应结果中找到对应的值，然后用公共的key和值组成新的字典
            new_actual_results = {common_key: actual_results[common_key]}
            # 比较新的字典和预期结果字典是否相等 | operator.eq(new_actual_results, expected_results) 是用于比较两个对象是否相等的函数式写法
            eq_assert = operator.eq(new_actual_results, expected_results)
            if eq_assert:
                flag += 1
                allure.attach(f"not_equal_assert断言失败：预期的{common_key} = {expected_results[common_key]}",
                              "不相等断言失败", allure.attachment_type.TEXT)
                logs.info(
                    f"not_equal_assert断言成功:预期的{common_key} = {new_actual_results[common_key]},响应结果为：{actual_results}")
            else:

                allure.attach(f"not_equal_assert断言成功：预期的{common_key} ！= {expected_results[common_key]}",
                              "相等断言失败", allure.attachment_type.TEXT)
                logs.info(
                    f"not_equal_assert断言成功:预期的{common_key} ！= {expected_results[common_key]},响应结果为：{actual_results}")
        else:
            raise TypeError('相等断言--类型错误，预期结果和接口实际响应结果必须为字典类型！')
        return flag

    def assert_mysql_data(self, expected_results):
        """
        数据库断言
        :param expected_results: 预期结果，yaml文件的SQL语句
        :return: 返回flag标识，0表示正常，非0表示测试不通过
        """
        flag = 0
        conn = ConnectMysql()
        db_value = conn.query_all(expected_results)
        if db_value != ():
            logs.info("数据库断言成功")
            allure.attach(f"数据库断言成功：sql:{expected_results}可以查询出结果，结果为：{db_value}",
                          "数据库断言成功", allure.attachment_type.TEXT)
        else:
            flag += 1
            logs.error("数据库断言失败，请检查数据库是否存在该数据！")
            allure.attach(f"数据库断言失败：sql:{expected_results}查询结果为空",
                          "数据库断言失败", allure.attachment_type.TEXT)
        return flag

    # 循环断言预期结果中的每一项
    def assert_result(self, expected, response, status_code):
        """
        断言，通过断言all_flag标记，all_flag==0表示测试通过，否则为失败
        :param expected: 预期结果
        :param response: 实际响应结果
        :param status_code: 响应code码
        :return:
        """
        all_flag = 0
        try:
            for ex in expected:
                for ex_key,ex_value in ex.items():
                    if ex_key == "contains":
                        flag =self.contains_assert(ex_value,response)
                        all_flag = all_flag+flag
                    elif ex_key == "equal":
                        flag = self.equal_assert(ex_value,response)
                        all_flag = all_flag+flag
                    elif ex_key == "ne":
                        flag = self.not_equal_assert(ex_value,response)
                        all_flag = all_flag + flag
                    elif ex_key =="db_query":
                        flag = self.assert_mysql_data(ex_value)
                        all_flag = all_flag + flag
                    elif ex_key =="contains_status_code":
                        flag = self.eq_status_code(ex_value,status_code)
                        all_flag = all_flag + flag
                    else:
                        logs.error("不支持此种断言方式")
                        assert False
        except Exception as e:
            logs.error(f"接口断言方法异常 异常：{e}")
            raise e
        if all_flag == 0:
            logs.info("测试成功")
            assert True
        else:
            logs.error("测试失败")
            assert False






